package com.example.protocol.raft;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

public class RaftNode {
    private static final Logger logger = LoggerFactory.getLogger(RaftNode.class);

    private State state = State.FOLLOWER;
    private int currentTerm = 0;
    private int votedFor = -1;
    private List<LogEntry> log = new ArrayList<>();
    private int commitIndex = 0;
    private int lastApplied = 0;
    private List<RaftNode> peers;
    private ReentrantLock lock = new ReentrantLock();

    public RaftNode(List<RaftNode> peers) {
        this.peers = peers;
        logger.info("RaftNode initialized with {} peers.", peers.size());
    }

    public void startElection() {
        lock.lock();
        try {
            state = State.CANDIDATE;
            currentTerm++;
            votedFor = getId();
            int votes = 1; // vote for self
            logger.info("Node {} started election for term {}.", getId(), currentTerm);

            for (RaftNode peer : peers) {
                if (peer.requestVote(currentTerm, getId())) {
                    votes++;
                }
            }

            if (votes > peers.size() / 2) {
                state = State.LEADER;
                logger.info("Node {} became leader for term {}.", getId(), currentTerm);
                // Send heartbeats to maintain leadership
                for (RaftNode peer : peers) {
                    peer.appendEntries(currentTerm, getId(), commitIndex, log);
                }
            } else {
                state = State.FOLLOWER;
                logger.info("Node {} failed to become leader for term {}.", getId(), currentTerm);
            }
        } finally {
            lock.unlock();
        }
    }

    public boolean requestVote(int term, int candidateId) {
        lock.lock();
        try {
            if (term > currentTerm && (votedFor == -1 || votedFor == candidateId)) {
                currentTerm = term;
                votedFor = candidateId;
                logger.info("Node {} voted for candidate {} in term {}.", getId(), candidateId, term);
                return true;
            }
            logger.info("Node {} rejected vote request from candidate {} in term {}.", getId(), candidateId, term);
            return false;
        } finally {
            lock.unlock();
        }
    }

    public boolean appendEntries(int term, int leaderId, int prevLogIndex, List<LogEntry> entries) {
        lock.lock();
        try {
            if (term >= currentTerm) {
                currentTerm = term;
                state = State.FOLLOWER;
                log.addAll(entries);
                commitIndex = log.size() - 1;
                logger.info("Node {} accepted entries from leader {} in term {}.", getId(), leaderId, term);
                return true;
            }
            logger.info("Node {} rejected entries from leader {} in term {}.", getId(), leaderId, term);
            return false;
        } finally {
            lock.unlock();
        }
    }

    private int getId() {
        return peers.indexOf(this);
    }

    public State getState() {
        return state;
    }
}
